package edu.cicese.alz;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.LookupPaintScale;
import org.jfree.chart.renderer.xy.XYBlockRenderer;
import org.jfree.chart.title.PaintScaleLegend;
import org.jfree.data.xy.DefaultXYZDataset;
import org.jfree.data.xy.XYZDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;

import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Dimension;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;

/**
 * Created by: Eduardo Quintana Contreras
 * Date: 13/03/12
 * Time: 02:55 PM
 */
public class NFCChart extends ApplicationFrame {

	private int userID = 0;
	private int periodMs = 60000;
	private long beginDate, endDate;

	public NFCChart(int userID) {
		super("NFC Chart: " + userID);

		this.userID = userID;
		JPanel localJPanel = new ChartPanel(createChart(createDatasetFromFiles()));
		localJPanel.setPreferredSize(new Dimension(1500, 750));
		setContentPane(localJPanel);
	}

	public NFCChart(ResultSet resultSet, int userID, long beginDate, long endDate) {
		super("NFC Chart: " + userID);

		this.userID = userID;
		this.beginDate = beginDate;
		this.endDate = endDate;
		JPanel localJPanel = new ChartPanel(createChart(createDataset(resultSet)));
		localJPanel.setPreferredSize(new Dimension(1500, 750));
		setContentPane(localJPanel);
	}

	private XYZDataset createDataset(ResultSet resultSet) {
		DefaultXYZDataset localDefaultXYZDataset = new DefaultXYZDataset();
		int periods = 1440;

		ArrayList<ActivityMeasure> activities = new ArrayList<>();
		try {
			while (resultSet.next()) {
				System.out.println(resultSet.getInt(2));
				activities.add(new ActivityMeasure(resultSet.getInt(2), resultSet.getLong(3)));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		System.out.println(activities.size());

		int numberOfDays = TimeUtil.getDayDifference(beginDate, endDate);
		int dataSize = periods * numberOfDays;

		double[] xValues = new double[dataSize];
		double[] yValues = new double[dataSize];
		double[] zValues = new double[dataSize];

		Calendar cal = TimeUtil.getDayTimestamp(beginDate);
		long tsAux = cal.getTimeInMillis();
		long tsBegin = tsAux;

		if (!activities.isEmpty()) {
			for (int day = 0; day < numberOfDays; day++) {
				for (int period = 0; period < periods; period++) {
//					System.out.println(day + " " + period);
					xValues[(day * periods + period)] = tsBegin;
					yValues[(day * periods + period)] = period;
					zValues[(day * periods + period)] = getValue(activities, tsAux);

					tsAux += periodMs;
				}

				cal.add(Calendar.DAY_OF_MONTH, 1);
				tsBegin = tsAux = cal.getTimeInMillis();
			}
		}
		else {
			for (int day = 0; day < numberOfDays; day++) {
				for (int period = 0; period < periods; period++) {
					xValues[(day * periods + period)] = tsBegin;
					yValues[(day * periods + period)] = period;
					zValues[(day * periods + period)] = 0.5;
				}
				cal.add(Calendar.DAY_OF_MONTH, 1);
				tsBegin = cal.getTimeInMillis();
			}
		}

		localDefaultXYZDataset.addSeries("Series 1", new double[][]{xValues, yValues, zValues});
		saveToFile(new double[][]{xValues, yValues, zValues}, "D:\\_proyectoAlz\\analysis\\nfc charts\\" + userID + "\\", beginDate + "_" + endDate + ".alz");

		return localDefaultXYZDataset;
	}

	private JFreeChart createChart(XYZDataset xyzDataset) {
		DateAxis localDateAxis = new DateAxis("Date");
		localDateAxis.setLowerMargin(0.0D);
		localDateAxis.setUpperMargin(0.0D);
		localDateAxis.setInverted(true);

//		DateAxis localNumberAxis = new DateAxis("Hour");
//		localNumberAxis.setTickUnit(new DateTickUnit(DateTickUnitType.MINUTE, 10));
//		DateFormat chartFormatter = new SimpleDateFormat("yyyy/MM/dd HH:mm");
//		localNumberAxis.setDateFormatOverride(chartFormatter);

		NumberAxis localNumberAxis = new NumberAxis("Hour");
//		DateAxis localNumberAxis = new DateAxis("Hour");
		localNumberAxis.setUpperMargin(0.0D);
		localNumberAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
//		localNumberAxis.setTickUnit(new DateTickUnit(DateTickUnitType.MINUTE, 1));

		XYBlockRenderer localXYBlockRenderer = new XYBlockRenderer();
		localXYBlockRenderer.setBlockWidth(86400000.0D);
		localXYBlockRenderer.setBlockAnchor(RectangleAnchor.BOTTOM_LEFT);
		LookupPaintScale localLookupPaintScale = new LookupPaintScale(0.5, 6.5, Color.BLUE);
		localLookupPaintScale.add(0.5, Color.WHITE);
		localLookupPaintScale.add(1.5, Color.BLACK);
		localLookupPaintScale.add(2.5, Color.YELLOW);
		localLookupPaintScale.add(3.5, new Color(228, 122, 255));
		localLookupPaintScale.add(4.5, Color.RED);
		localLookupPaintScale.add(5.5, Color.GREEN);
//		localLookupPaintScale.add(4.5, new Color(158, 175, 255));
		localXYBlockRenderer.setPaintScale(localLookupPaintScale);
		

//		LookupPaintScale localLookupPaintScale = new LookupPaintScale(0.5, 5.5, Color.BLACK);
//		localLookupPaintScale.add(0.5, Color.BLACK);
//		localLookupPaintScale.add(1.5, Color.BLACK);
//		localLookupPaintScale.add(2.5, Color.BLACK);
//		localLookupPaintScale.add(3.5, Color.BLACK);
//		localLookupPaintScale.add(4.5, Color.RED);
//		localXYBlockRenderer.setPaintScale(localLookupPaintScale);

		XYPlot localXYPlot = new XYPlot(xyzDataset, localDateAxis, localNumberAxis, localXYBlockRenderer);
		localXYPlot.setOrientation(PlotOrientation.HORIZONTAL);
		localXYPlot.setBackgroundPaint(Color.WHITE);
		localXYPlot.setRangeGridlinePaint(Color.WHITE);
		localXYPlot.setAxisOffset(new RectangleInsets(5.0D, 5.0D, 5.0D, 5.0D));

		JFreeChart localJFreeChart = new JFreeChart("NFC Chart: " + userID, localXYPlot);
		localJFreeChart.removeLegend();
		localJFreeChart.setBackgroundPaint(Color.WHITE);

//		SymbolAxis localSymbolAxis = new SymbolAxis(null, new String[]{"", "No Data", "Get up/Go to bed", "Bathe", "Get dress", "Go out", "X"});
		SymbolAxis localSymbolAxis = new SymbolAxis(null, new String[]{"", "No Data", "Preguntas repetitivas", "Agresión/Agitación", "Comp. problemático", "Tristeza/Depresión", "Ansiedad/Preocupación"});
		localSymbolAxis.setRange(0.5D, 6.5D);
		localSymbolAxis.setPlot(new PiePlot());
		localSymbolAxis.setGridBandsVisible(false);
		PaintScaleLegend localPaintScaleLegend = new PaintScaleLegend(localLookupPaintScale, localSymbolAxis);
		localPaintScaleLegend.setMargin(new RectangleInsets(3.0D, 10.0D, 3.0D, 10.0D));
		localPaintScaleLegend.setPosition(RectangleEdge.BOTTOM);
		localPaintScaleLegend.setAxisOffset(5.0D);
		localJFreeChart.addSubtitle(localPaintScaleLegend);
		ChartUtilities.applyCurrentTheme(localJFreeChart);
		return localJFreeChart;
	}

	private double getValue(ArrayList<ActivityMeasure> activities, long tsAux) {
		long tsA = tsAux;
		long tsB = tsAux + periodMs;

		double value = 0.5;
		long aTimestamp;
		boolean nfcScanned = false;

		while (!activities.isEmpty()) {
			aTimestamp = activities.get(0).getTimestamp();

			if (aTimestamp > tsB) {
				break;
			}

			if (aTimestamp > tsA && aTimestamp <= tsB) {
				if (!nfcScanned) {
					switch (activities.get(0).getMessageID()) {
						case 1:     // Get up/Go to bed
							value = 1.5;
							nfcScanned = true;
							break;
						case 3:     // Bathe
							value = 2.5;
							nfcScanned = true;
							break;
						case 6:     // Get dressed
							value = 3.5;
							nfcScanned = true;
							break;
						case 16:    // Go out
						case 17:
						case 18:
						case 19:
						case 20:
							value = 4.5;
							nfcScanned = true;
							break;


						case 21:
							value = 1.5;
							nfcScanned = true;
							break;
						case 22:
							value = 2.5;
							nfcScanned = true;
							break;
						case 23:
							value = 3.5;
							nfcScanned = true;
							break;
						case 24:
							value = 4.5;
							nfcScanned = true;
							break;
						case 25:
							value = 5.5;
							nfcScanned = true;
							break;
					}
				}
			}

			activities.remove(0);
		}

		return value;
	}

	private XYZDataset createDatasetFromFiles() {
		DefaultXYZDataset localDefaultXYZDataset = new DefaultXYZDataset();

		File dir = new File("D:\\_proyectoAlz\\analysis\\nfc charts\\" + userID);
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.toLowerCase().endsWith(".alz");
			}
		};
		File[] files = dir.listFiles(filter);

		if (files != null) {
			double[][] values = new double[3][288 * files.length];
			int day = 0;

			for (File file : files) {
				try {
					double[][] valuesAux = loadData(file.getCanonicalPath());
					for (int i = 0; i < 288; i++) {
						values[0][day * 288 + i] = valuesAux[0][i];
						values[1][day * 288 + i] = valuesAux[1][i];
						values[2][day * 288 + i] = valuesAux[2][i];
					}
					day++;

				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			localDefaultXYZDataset.addSeries("Series 1", values);
		}

		return localDefaultXYZDataset;
	}

	private static void saveToFile(Object object, String folder, String filename) {
		try {
			File file = new File(folder);
			file.mkdirs();
			ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(new File(folder + filename)));
			stream.writeObject(object);
			stream.close();
		}
		catch (IOException e) { e.printStackTrace(); }
	}

	private static double[][] loadData(String filename){
		File file = new File(filename);
		if (file.exists()) {
			try {
				ObjectInputStream str = new ObjectInputStream(new FileInputStream(file));
				return (double[][]) str.readObject();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

}
